package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.factory.FactoryBean;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用于存储通过FactoryBean创建的单例，FactoryBean本身则存储在DefaultSingletonBeanRegistry中
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry{
    //存储FactoryBean创建的目标Bean
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);


    /**
     * 从FactoryBean中获取目标对象，不存在则创建
     * @param beanName          Bean的名称，不带&
     */
    protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName) {
        //检查FactoryBean是否在Spring容器中
        if (factory.isSingleton() && containsSingleton(beanName)) {
            synchronized (getSingletonMutex()) {
                //1) 尝试从缓存中获取目标对象
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    //2) 调用getObject方法创建目标对象
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    //3) 将目标对象加入到缓存
                    this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                }
                return (object != NULL_OBJECT ? object : null);
            }
        }
        //非单例Bean，无需缓存，直接创建新对象
        return doGetObjectFromFactoryBean(factory, beanName);
    }


    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName){
        Object object;
        try {
            object = factory.getObject();
        }catch (Exception e){
            throw new BeansException("使用工厂Bean创建实例失败，beanName[" + beanName + "]", e);
        }
        return object;
    }


    protected Object getCachedObjectForFactoryBean(String beanName) {
        Object object = this.factoryBeanObjectCache.get(beanName);
        return (object != NULL_OBJECT ? object : null);
    }


    //如果单例是FactoryBean，则返回实际的对象类型，否则返回null
    protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
        try{
            return factoryBean.getObjectType();
        }catch (Exception e){
            return null;
        }
    }


    @Override
    protected void removeSingleton(String beanName) {
        synchronized (getSingletonMutex()) {
            super.removeSingleton(beanName);
            this.factoryBeanObjectCache.remove(beanName);
        }
    }
}
